/*
    Copyright 2011 Peter Pratscher
 
    This file is part of Surveillance.NET.

    Surveillance.NET is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    Surveillance.NET is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Surveillance.NET. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using AForge.Controls;
using AForge.Video.DirectShow;
using AForge.Vision.Motion;
using System.Runtime.Serialization;

namespace SurveillanceDotNet.Model
{
    public delegate void AlarmEventHandler(object sender, EventArgs e);
    public delegate void NewSurveillancePictureHandler(object sender, EventArgs e);

    [Serializable]
    public class SurveillanceCamera : PropertyChangedBase, ISerializable
    {
        public string Name { get; set; }
        public VideoSourcePlayer Source { get; private set; }

        IMotionDetector motionDetectionAlgo = new TwoFramesDifferenceDetector(true);
        IMotionProcessing motionProcessingAlgo = new MotionAreaHighlighting();
        MotionDetector motionDetector;

        private int _ScaleFactor;
        public int ScaleFactor
        {
            get
            {
                return _ScaleFactor;
            }
            set
            {
                _ScaleFactor = value;
            }
        }
        public int AlarmLevel { get; set; }

        public int MotionLevel { get; set; }

        public Bitmap CurrentFrame { get; private set; }

        private string _Moniker;

        private string _Status;
        public string Status
        {
            get
            {
                return _Status;
            }
            private set
            {
                _Status = value;
                OnPropertyChanged("Status");
            }
        }

        public event AlarmEventHandler Alarm;
        public event NewSurveillancePictureHandler NewSurveillancePicture;

        private volatile bool shouldStop = false;

        public SurveillanceCamera(string moniker)
        {
            this._Moniker = moniker;
            this.Source = new VideoSourcePlayer();
            this.Source.VideoSource = new VideoCaptureDevice(moniker);

            this.Source.NewFrame += new VideoSourcePlayer.NewFrameHandler(Source_NewFrame);

            this.Status = "Stopped";
            this.ScaleFactor = 100;
            this.AlarmLevel = 100;

            motionDetector = new MotionDetector(motionDetectionAlgo, motionProcessingAlgo);
        }

        void Source_NewFrame(object sender, ref Bitmap image)
        {
            if (i++ % 50 == 0) Debug.WriteLine(DateTime.Now.ToString() + " - Frame received");

            if (shouldStop) return;

            float motionLevel = motionDetector.ProcessFrame(image);

            int motionLevelNormalized = (int)(motionLevel * ScaleFactor);

            if (motionLevelNormalized > 100) motionLevelNormalized = 100;

            MotionLevel = motionLevelNormalized;

            if (motionLevelNormalized >= AlarmLevel)
            {
                OnAlarm(new EventArgs());
                this.Status = "Alarm";
            }
            else
            {
                this.Status = "Started";
            }

            if (this.CurrentFrame != null)
                this.CurrentFrame.Dispose();
            this.CurrentFrame = (Bitmap)image.Clone();

            OnNewSurveillancePicture(new EventArgs());
        }

        protected virtual void OnAlarm(EventArgs e)
        {
            if (Alarm != null)
            {
                Alarm(this, e);
            }
        }

        protected virtual void OnNewSurveillancePicture(EventArgs e)
        {
            if (NewSurveillancePicture != null && !shouldStop)
            {
                NewSurveillancePicture(this, e);
            }
        }

        public void StartMonitoring()
        {
            if (this.Status != "Stopped") StopMonitoring();

            shouldStop = false;

            this.Source.Start();
            this.Status = "Started";

        }

        int i = 0;

        public void StopMonitoring()
        {
            shouldStop = true;

            Application.DoEvents();

            this.Source.SignalToStop();

            // wait 2 seconds until camera stops
            for (int i = 0; (i < 50) && (this.Source.IsRunning); i++)
            {
                Thread.Sleep(100);
                Application.DoEvents();
            }

            if (this.Source.IsRunning)
                Source.Stop();

            this.Status = "Stopped";
        }

        protected SurveillanceCamera(SerializationInfo info, StreamingContext context)
        {
            _Moniker = info.GetString("_Moniker");
            Name = info.GetString("Name");
            ScaleFactor = info.GetInt32("ScaleFactor");
            AlarmLevel = info.GetInt32("AlarmLevel");
            MotionLevel = info.GetInt32("MotionLevel");
            Source = new VideoSourcePlayer();
            Source.VideoSource = new VideoCaptureDevice(_Moniker);

            Source.NewFrame += new VideoSourcePlayer.NewFrameHandler(Source_NewFrame);

            Status = "Stopped";

            motionDetector = new MotionDetector(motionDetectionAlgo, motionProcessingAlgo);
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("_Moniker", _Moniker);
            info.AddValue("Name", Name);
            info.AddValue("ScaleFactor", ScaleFactor);
            info.AddValue("AlarmLevel", AlarmLevel);
            info.AddValue("MotionLevel", MotionLevel);
        }
    }
}
